feat: support explicit raw secret values - #2873
feat: support explicit raw secret values#2873Mikhail Shirkov (shirkevich) wants to merge 5 commits into
Conversation
|
Tip Atmos Pro
No affected stacks workflow was detected for this pull request. |
Dependency Review✅ No vulnerabilities or license issues found.Scanned FilesNone |
3eb7b01 to
9391ee6
Compare
1dbeaa4 to
100fe74
Compare
9391ee6 to
0eb508d
Compare
100fe74 to
0ad5365
Compare
0eb508d to
823a437
Compare
0ad5365 to
22cf993
Compare
823a437 to
a6274b0
Compare
22cf993 to
c1b63f7
Compare
a6274b0 to
910bc16
Compare
c1b63f7 to
39bc2dd
Compare
910bc16 to
0728075
Compare
39bc2dd to
e2883a5
Compare
0728075 to
3ed670d
Compare
e2883a5 to
7922d6b
Compare
3ed670d to
6fcb568
Compare
7922d6b to
0875add
Compare
e372a2c to
bb4156f
Compare
|
CodeRabbit (@coderabbitai) review |
✅ Action performedReview finished.
|
📝 WalkthroughWalkthroughThe change adds component-less global secret setting and introduces ChangesSecret capabilities
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant YAML
participant ParseSecret
participant SecretResolver
participant Provider
participant Store
participant Helm
YAML->>ParseSecret: parse secret name and modifiers
ParseSecret->>SecretResolver: provide parsed raw option
SecretResolver->>Provider: request raw or decoded value
Provider->>Store: retrieve secret payload
Store-->>Provider: return payload
Provider-->>SecretResolver: return resolved value
SecretResolver-->>Helm: provide chart value
Helm-->>YAML: render environment variable
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
cmd/secret/enumerate.go (1)
98-114: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winAdd
ComponentTypeas a sort tie-breaker.Line 114 makes
ComponentTypepart of the selected scope. The sort compares onlyStackandComponent. If two component types use the same component name, map iteration can determine which typefindGlobalSetContextselects. The command can then load a different component configuration and write the secret through the wrong context.Sort by
ComponentTypeafterComponent, or require--typewhen this ambiguity exists. Add a regression test with the same component name in two types.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/secret/enumerate.go` around lines 98 - 114, Update the scope-entry sorting used before findGlobalSetContext to compare ComponentType after Stack and Component, ensuring entries with identical names have deterministic ordering. Add a regression test covering identical component names across two component types and verify the selected context remains consistent.pkg/secrets/resolver.go (1)
68-88: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winDo not use
Defaultfor unsupported raw retrieval.When the provider returns
providers.ErrRawNotSupported, Lines 84-87 returnopts.Default. The secret can exist, but the requested raw capability is unavailable. This hides a configuration error and can silently use a fallback value.Apply
Defaultonly to missing-secret errors. PreserveErrRawNotSupportedthrough the wrapped result. Add a resolver test forraw | defaultwith a structured-only provider.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/secrets/resolver.go` around lines 68 - 88, The error handling in the resolver’s raw retrieval flow must not apply opts.Default when the provider returns providers.ErrRawNotSupported; restrict the default fallback to missing-secret errors and preserve ErrRawNotSupported through the existing wrapped result. Add a resolver test covering raw | default with a structured-only provider.
🧹 Nitpick comments (3)
cmd/secret/set_test.go (1)
127-194: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse a table-driven test for these lookup scenarios.
The repeated subtests cover one function with the same setup pattern. Put the entries, scope, expected component, and expected error in test cases. This keeps future scope cases consistent.
As per coding guidelines, “Use table-driven tests for testing multiple scenarios in Go.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/secret/set_test.go` around lines 127 - 194, Refactor TestFindGlobalSetContext into a table-driven test covering enumeration errors, missing matches, inconsistent declarations, and identical declarations. Store each case’s scope entries, requested scope, secret name, expected component/type, and expected error in the test table, then run them through a single subtest loop while preserving the existing assertions and overrideEnumerateScopes setup.Source: Coding guidelines
pkg/function/parser/parser_test.go (1)
196-224: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse table-driven cases for parser scenarios.
This test covers multiple input forms with separate assertions. Use one table with input, expected
SecretArgs, and expected error fields. IncludeSERVICE_CONFIG | path "" | rawas an invalid case.As per coding guidelines, “Use table-driven tests for testing multiple scenarios in Go.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/function/parser/parser_test.go` around lines 196 - 224, The TestParseSecret test should use a single table-driven case list containing each valid and invalid input, expected SecretArgs, and expected error status/details. Replace the separate assertions and invalid-input loop while preserving the existing expectations, and add SERVICE_CONFIG | path "" | raw as an invalid case.Source: Coding guidelines
pkg/secrets/providers/provider.go (1)
73-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the project static error catalog.
ErrRawNotSupportedcreates a new sentinel directly witherrors.New. Define the static error inerrors/errors.go, then wrap or expose it from this boundary as needed. Preserveerrors.Isbehavior.As per coding guidelines, “Wrap all errors with static errors from
errors/errors.go.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/secrets/providers/provider.go` around lines 73 - 74, Replace the locally constructed ErrRawNotSupported sentinel with the corresponding static error defined in errors/errors.go, then expose or wrap that catalog error from the provider boundary while preserving errors.Is compatibility for callers.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmd/secret/set.go`:
- Around line 99-132: Update findGlobalSetContext to validate each global
declaration’s effective backend coordinate using its component context, rather
than comparing raw declarations alone. Resolve or reject declarations whose
Declaration.Reference depends on atmos_component, and return the existing
componentRequiredForSet error when effective coordinates differ; add coverage
for identical component-dependent declarations.
In `@pkg/function/parser/parser.go`:
- Around line 375-389: Track path-option presence separately from result.Path in
the parser handling around the path and raw cases, so an explicitly empty path
is distinguishable from an omitted path. Update the mutual-exclusion check to
reject raw whenever path was specified, including path "", and add a regression
case for SERVICE_CONFIG | path "" | raw.
In `@pkg/store/providers/google_secret_manager_store.go`:
- Around line 478-484: Add direct unit-test cases for GSMStore.GetRaw covering
both JSON and non-JSON secret payloads. Assert the returned value exactly
matches the backend payload, including its original encoding and content,
without decoding or transformation; retain existing error assertions and use the
established GSM test fixtures and backend mocks.
In `@pkg/store/providers/keychain_store.go`:
- Around line 163-166: Update the JSON string decoding in GetRaw to unmarshal
into a string pointer, returning the decoded value only when the pointer is
non-nil. Preserve the original raw payload for JSON null while retaining the
existing return behavior for non-null strings.
---
Outside diff comments:
In `@cmd/secret/enumerate.go`:
- Around line 98-114: Update the scope-entry sorting used before
findGlobalSetContext to compare ComponentType after Stack and Component,
ensuring entries with identical names have deterministic ordering. Add a
regression test covering identical component names across two component types
and verify the selected context remains consistent.
In `@pkg/secrets/resolver.go`:
- Around line 68-88: The error handling in the resolver’s raw retrieval flow
must not apply opts.Default when the provider returns
providers.ErrRawNotSupported; restrict the default fallback to missing-secret
errors and preserve ErrRawNotSupported through the existing wrapped result. Add
a resolver test covering raw | default with a structured-only provider.
---
Nitpick comments:
In `@cmd/secret/set_test.go`:
- Around line 127-194: Refactor TestFindGlobalSetContext into a table-driven
test covering enumeration errors, missing matches, inconsistent declarations,
and identical declarations. Store each case’s scope entries, requested scope,
secret name, expected component/type, and expected error in the test table, then
run them through a single subtest loop while preserving the existing assertions
and overrideEnumerateScopes setup.
In `@pkg/function/parser/parser_test.go`:
- Around line 196-224: The TestParseSecret test should use a single table-driven
case list containing each valid and invalid input, expected SecretArgs, and
expected error status/details. Replace the separate assertions and invalid-input
loop while preserving the existing expectations, and add SERVICE_CONFIG | path
"" | raw as an invalid case.
In `@pkg/secrets/providers/provider.go`:
- Around line 73-74: Replace the locally constructed ErrRawNotSupported sentinel
with the corresponding static error defined in errors/errors.go, then expose or
wrap that catalog error from the provider boundary while preserving errors.Is
compatibility for callers.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5ddaa5fb-4e7d-4fa4-be16-447fd3de79dc
📒 Files selected for processing (32)
cmd/secret/enumerate.gocmd/secret/set.gocmd/secret/set_test.gocmd/secret/shared.gopkg/component/helm/secret_values_integration_test.gopkg/function/parser/parser.gopkg/function/parser/parser_test.gopkg/function/secret.gopkg/io/global.gopkg/secrets/providers/provider.gopkg/secrets/providers/store.gopkg/secrets/providers/store_test.gopkg/secrets/resolver.gopkg/secrets/resolver_test.gopkg/secrets/types.gopkg/store/providers/aws_secrets_manager_store.gopkg/store/providers/aws_secrets_manager_store_endpoint_test.gopkg/store/providers/aws_ssm_param_store.gopkg/store/providers/azure_keyvault_store.gopkg/store/providers/azure_keyvault_store_test.gopkg/store/providers/github_actions_store.gopkg/store/providers/google_secret_manager_store.gopkg/store/providers/google_secret_manager_store_test.gopkg/store/providers/keychain_store.gopkg/store/providers/keychain_store_test.gopkg/store/store.gotests/fixtures/scenarios/helm-secret-values/atmos.yamltests/fixtures/scenarios/helm-secret-values/components/helm/secret-values/Chart.yamltests/fixtures/scenarios/helm-secret-values/components/helm/secret-values/templates/deployment.yamltests/fixtures/scenarios/helm-secret-values/stacks/deploy/dev.yamlwebsite/docs/cli/commands/secret/set.mdxwebsite/docs/functions/yaml/secret.mdx
|
CodeRabbit (@coderabbitai) review\n\nPlease review current head |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
cmd/secret/set_test.go (1)
127-194: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse a table-driven test for the context-resolution cases.
The four subtests repeat the setup, invocation, and error assertions. Put the cases in a table with expected component, type, and error state.
As per coding guidelines, “Use table-driven tests for testing multiple scenarios in Go.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/secret/set_test.go` around lines 127 - 194, Refactor TestFindGlobalSetContext into a table-driven test covering the four existing context-resolution scenarios. Define each case with its scope entries, enumeration error, expected component and component type, and whether an error is expected; iterate with subtests, applying overrideEnumerateScopes and invoking findGlobalSetContext once per case while preserving the existing assertions and outcomes.Source: Coding guidelines
pkg/function/parser/parser_test.go (1)
196-225: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse table-driven tests for parser scenarios.
Both tests exercise multiple independent parser scenarios. Put each input and expected result or error assertion in a test table.
pkg/function/parser/parser_test.go#L196-L225: table-drive valid and invalidParseSecretinputs.pkg/secrets/resolver_test.go#L181-L196: table-drive raw, default, compact syntax, conflict, and empty-name cases.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/function/parser/parser_test.go` around lines 196 - 225, Convert TestParseSecret in pkg/function/parser/parser_test.go (lines 196-225) into table-driven cases covering each valid input with its expected SecretArgs or field assertions, plus invalid inputs expecting errors. Also convert the raw, default, compact syntax, conflict, and empty-name scenarios in pkg/secrets/resolver_test.go (lines 181-196) into a table-driven test, preserving each scenario’s existing expectations.Source: Coding guidelines
pkg/secrets/resolver_test.go (1)
154-173: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a raw missing-secret fallback test.
This test confirms that
ErrRawNotSupporteddoes not use the default. Add a case where raw retrieval returns a missing-secret error and assert thatraw | default "fallback"returns"fallback".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/secrets/resolver_test.go` around lines 154 - 173, Add a test alongside TestResolve_RawDefaultDoesNotHideUnsupportedCapability that configures the mock store to return the missing-secret error for DATADOG_API_KEY, then resolves the raw secret expression with default "fallback" and asserts it returns "fallback" without an error. Keep the existing unsupported-capability test unchanged.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmd/secret/set_test.go`:
- Around line 122-125: Update the test around runSecretSubcommand and its
loadServiceFn mock to capture the secretScope argument passed to the loader,
then assert that its ComponentType is "helm" in addition to the existing
call-count checks. Keep the assertion behavior-focused and table-driven if the
surrounding tests support that pattern.
In `@pkg/component/helm/secret_values_integration_test.go`:
- Line 52: Refactor
TestNativeHelmSecretRawAndStructuredValuesMaskIndentedMultilineValues into named
helpers so it stays under 60 lines and separates rendering from masking
assertions. In the fallback manifest path, use require.NotEmpty before accessing
containers[0], then validate manifest and container types with require.IsType
before asserting and reading env; ensure malformed fixtures fail through test
assertions rather than panicking.
In `@pkg/function/parser/parser.go`:
- Line 340: Correct the grammar comment for ParseSecret to show that path
expression and raw are alternatives rather than independently optional, matching
the validation that rejects using both together in the parser logic.
In `@pkg/secrets/providers/store_test.go`:
- Around line 106-129: Add a test alongside
TestStoreProvider_GetRawFallsBackForTextOnly using a mock store that implements
store.RawStore. Configure the raw-store method to return a raw payload, assert
GetRaw returns it without error, and do not set any Store.Get expectation so the
test verifies native RawStore delegation bypasses the fallback.
In `@pkg/store/providers/aws_secrets_manager_store.go`:
- Around line 296-311: Add repository-standard deferred perf.Track
instrumentation, using each method’s atmosConfig and fully qualified function
name, at the start of SecretsManagerStore.GetRaw in
pkg/store/providers/aws_secrets_manager_store.go:296-311, GSMStore.GetRaw in
pkg/store/providers/google_secret_manager_store.go:478-484, and
KeychainStore.GetRaw in pkg/store/providers/keychain_store.go:151-168; include
the required blank line after each instrumentation statement.
In `@pkg/store/providers/aws_ssm_param_store.go`:
- Around line 426-435: Update the inline comments above getKey and assumeRole in
the parameter-name retrieval flow so each comment ends with a period, without
changing the surrounding logic.
In `@pkg/store/providers/azure_keyvault_store.go`:
- Around line 369-389: Update AzureKeyVaultStore.GetRaw and Set to allow empty
stack and/or component values so getKey can resolve stack-scoped and global
coordinates consistently with GSMStore; retain key validation and existing error
handling. Ensure !secret NAME | raw works and component-less global writes are
supported, then add coverage for stack-scoped and global reads and writes.
---
Nitpick comments:
In `@cmd/secret/set_test.go`:
- Around line 127-194: Refactor TestFindGlobalSetContext into a table-driven
test covering the four existing context-resolution scenarios. Define each case
with its scope entries, enumeration error, expected component and component
type, and whether an error is expected; iterate with subtests, applying
overrideEnumerateScopes and invoking findGlobalSetContext once per case while
preserving the existing assertions and outcomes.
In `@pkg/function/parser/parser_test.go`:
- Around line 196-225: Convert TestParseSecret in
pkg/function/parser/parser_test.go (lines 196-225) into table-driven cases
covering each valid input with its expected SecretArgs or field assertions, plus
invalid inputs expecting errors. Also convert the raw, default, compact syntax,
conflict, and empty-name scenarios in pkg/secrets/resolver_test.go (lines
181-196) into a table-driven test, preserving each scenario’s existing
expectations.
In `@pkg/secrets/resolver_test.go`:
- Around line 154-173: Add a test alongside
TestResolve_RawDefaultDoesNotHideUnsupportedCapability that configures the mock
store to return the missing-secret error for DATADOG_API_KEY, then resolves the
raw secret expression with default "fallback" and asserts it returns "fallback"
without an error. Keep the existing unsupported-capability test unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 0d4e8a9a-25fa-4fc2-8cc8-21c66d6cb493
📒 Files selected for processing (34)
cmd/secret/enumerate.gocmd/secret/enumerate_test.gocmd/secret/set.gocmd/secret/set_test.gocmd/secret/shared.gopkg/component/helm/secret_values_integration_test.gopkg/function/parser/parser.gopkg/function/parser/parser_test.gopkg/function/secret.gopkg/io/global.gopkg/secrets/providers/provider.gopkg/secrets/providers/store.gopkg/secrets/providers/store_test.gopkg/secrets/resolver.gopkg/secrets/resolver_test.gopkg/secrets/scope_test.gopkg/secrets/types.gopkg/store/providers/aws_secrets_manager_store.gopkg/store/providers/aws_secrets_manager_store_endpoint_test.gopkg/store/providers/aws_ssm_param_store.gopkg/store/providers/azure_keyvault_store.gopkg/store/providers/azure_keyvault_store_test.gopkg/store/providers/github_actions_store.gopkg/store/providers/google_secret_manager_store.gopkg/store/providers/google_secret_manager_store_test.gopkg/store/providers/keychain_store.gopkg/store/providers/keychain_store_test.gopkg/store/store.gotests/fixtures/scenarios/helm-secret-values/atmos.yamltests/fixtures/scenarios/helm-secret-values/components/helm/secret-values/Chart.yamltests/fixtures/scenarios/helm-secret-values/components/helm/secret-values/templates/deployment.yamltests/fixtures/scenarios/helm-secret-values/stacks/deploy/dev.yamlwebsite/docs/cli/commands/secret/set.mdxwebsite/docs/functions/yaml/secret.mdx
| err := runSecretSubcommand(t, "set", "SHARED_TOKEN=v1", "--stack", "dev", "--type", "helm") | ||
| require.NoError(t, err) | ||
| require.Len(t, svc.setCalls, 1) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert the resolved component type.
This test passes if parseSetScope drops or overwrites --type. svc.Set does not expose the scope used by loadServiceFn.
Capture the secretScope passed to loadServiceFn, then assert that ComponentType is "helm".
As per coding guidelines, “Prefer behavior-focused, table-driven unit tests with mocks.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cmd/secret/set_test.go` around lines 122 - 125, Update the test around
runSecretSubcommand and its loadServiceFn mock to capture the secretScope
argument passed to the loader, then assert that its ComponentType is "helm" in
addition to the existing call-count checks. Keep the assertion behavior-focused
and table-driven if the surrounding tests support that pattern.
Source: Coding guidelines
| return s.Store.(store.RawStore).GetRaw(stack, component, key) | ||
| } | ||
|
|
||
| func TestNativeHelmSecretRawAndStructuredValuesMaskIndentedMultilineValues(t *testing.T) { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Split the test and validate manifest types before access.
The test exceeds the 60-line limit. The fallback also indexes containers[0] and uses unchecked type assertions. A malformed manifest will panic instead of reporting the failed fixture contract.
Extract rendering and masking assertions into helpers. Use require.NotEmpty and type assertions guarded by require.IsType before accessing env.
As per coding guidelines, “Safety precondition and fixture-count checks must fail loudly” and “Refactor functions exceeding ... 60 lines/40 statements into named, single-responsibility helpers.”
Also applies to: 106-113
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/component/helm/secret_values_integration_test.go` at line 52, Refactor
TestNativeHelmSecretRawAndStructuredValuesMaskIndentedMultilineValues into named
helpers so it stays under 60 lines and separates rendering from masking
assertions. In the fallback manifest path, use require.NotEmpty before accessing
containers[0], then validate manifest and container types with require.IsType
before asserting and reading env; ensure malformed fixtures fail through test
assertions rather than panicking.
Source: Coding guidelines
| return StoreGetArgs{Store: words[0], Key: words[1], Default: options.defaultValue, Query: options.query}, nil | ||
| } | ||
|
|
||
| // ParseSecret parses `name [| path expression] [| raw] [| default value]`. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Correct the ParseSecret grammar comment.
Line 340 presents path and raw as independently optional. Lines 390-391 reject their combination. Show them as alternatives.
Proposed fix
-// ParseSecret parses `name [| path expression] [| raw] [| default value]`.
+// ParseSecret parses `name [| path expression | raw] [| default value]`.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // ParseSecret parses `name [| path expression] [| raw] [| default value]`. | |
| // ParseSecret parses `name [| path expression | raw] [| default value]`. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/function/parser/parser.go` at line 340, Correct the grammar comment for
ParseSecret to show that path expression and raw are alternatives rather than
independently optional, matching the validation that rejects using both together
in the parser logic.
Source: Coding guidelines
| func TestStoreProvider_GetRawFallsBackForTextOnly(t *testing.T) { | ||
| ctrl := gomock.NewController(t) | ||
| defer ctrl.Finish() | ||
|
|
||
| mockStore := store.NewMockStore(ctrl) | ||
| mockStore.EXPECT().Get("prod", "api", "API_KEY").Return("v1", nil) | ||
| p := &storeProvider{name: "app", kind: "example/text", store: mockStore} | ||
|
|
||
| got, err := p.GetRaw(Coordinate{Stack: "prod", Component: "api", Key: "API_KEY"}) | ||
| require.NoError(t, err) | ||
| assert.Equal(t, "v1", got) | ||
| } | ||
|
|
||
| func TestStoreProvider_GetRawRejectsStructuredFallback(t *testing.T) { | ||
| ctrl := gomock.NewController(t) | ||
| defer ctrl.Finish() | ||
|
|
||
| mockStore := store.NewMockStore(ctrl) | ||
| mockStore.EXPECT().Get("prod", "api", "CONFIG").Return(map[string]any{"enabled": true}, nil) | ||
| p := &storeProvider{name: "app", kind: "example/structured", store: mockStore} | ||
|
|
||
| _, err := p.GetRaw(Coordinate{Stack: "prod", Component: "api", Key: "CONFIG"}) | ||
| require.ErrorIs(t, err, ErrRawNotSupported) | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add coverage for native store.RawStore delegation.
The tests cover only the fallback branch. Add a test where p.store implements store.RawStore. Assert that GetRaw returns the raw payload and does not call Store.Get.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/secrets/providers/store_test.go` around lines 106 - 129, Add a test
alongside TestStoreProvider_GetRawFallsBackForTextOnly using a mock store that
implements store.RawStore. Configure the raw-store method to return a raw
payload, assert GetRaw returns it without error, and do not set any Store.Get
expectation so the test verifies native RawStore delegation bypasses the
fallback.
Source: Coding guidelines
| // GetRaw retrieves the original Secrets Manager string without JSON decoding. | ||
| func (s *SecretsManagerStore) GetRaw(stack string, component string, key string) (string, error) { | ||
| if key == "" { | ||
| return nil, store.ErrEmptyKey | ||
| return "", store.ErrEmptyKey | ||
| } | ||
|
|
||
| if err := s.ensureClient(); err != nil { | ||
| return nil, err | ||
| return "", err | ||
| } | ||
|
|
||
| secretID, err := s.getKey(stack, component, key) | ||
| if err != nil { | ||
| return nil, fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | ||
| return "", fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | ||
| } | ||
|
|
||
| return s.getByID(secretID) | ||
| return s.getRawByID(secretID) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Instrument all new GetRaw methods.
Each new public raw-read operation lacks the required performance tracker.
pkg/store/providers/aws_secrets_manager_store.go#L296-L311: Add repository-standardperf.Trackinstrumentation toSecretsManagerStore.GetRaw.pkg/store/providers/google_secret_manager_store.go#L478-L484: Add repository-standardperf.Trackinstrumentation toGSMStore.GetRaw.pkg/store/providers/keychain_store.go#L151-L168: Add repository-standardperf.Trackinstrumentation toKeychainStore.GetRaw.
As per coding guidelines, “Add defer perf.Track(atmosConfig, "pkg.FuncName")() plus a blank line to public functions.”
📍 Affects 3 files
pkg/store/providers/aws_secrets_manager_store.go#L296-L311(this comment)pkg/store/providers/google_secret_manager_store.go#L478-L484pkg/store/providers/keychain_store.go#L151-L168
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/store/providers/aws_secrets_manager_store.go` around lines 296 - 311, Add
repository-standard deferred perf.Track instrumentation, using each method’s
atmosConfig and fully qualified function name, at the start of
SecretsManagerStore.GetRaw in
pkg/store/providers/aws_secrets_manager_store.go:296-311, GSMStore.GetRaw in
pkg/store/providers/google_secret_manager_store.go:478-484, and
KeychainStore.GetRaw in pkg/store/providers/keychain_store.go:151-168; include
the required blank line after each instrumentation statement.
Source: Coding guidelines
| // Construct the full parameter name using getKey | ||
| paramName, err := s.getKey(stack, component, key) | ||
| if err != nil { | ||
| return nil, fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | ||
| return "", fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | ||
| } | ||
|
|
||
| // Assume the read role if specified | ||
| cfg, err := s.assumeRole(ctx, s.readRoleArn) | ||
| if err != nil { | ||
| return nil, fmt.Errorf(errWrapFormat, store.ErrAssumeRole, err) | ||
| return "", fmt.Errorf(errWrapFormat, store.ErrAssumeRole, err) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Terminate the inline comments with periods.
Line 426 and Line 432 add comments without final periods. This violates the Go comment rule.
Proposed fix
- // Construct the full parameter name using getKey
+ // Construct the full parameter name using getKey.
@@
- // Assume the read role if specified
+ // Assume the read role if specified.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Construct the full parameter name using getKey | |
| paramName, err := s.getKey(stack, component, key) | |
| if err != nil { | |
| return nil, fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | |
| return "", fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | |
| } | |
| // Assume the read role if specified | |
| cfg, err := s.assumeRole(ctx, s.readRoleArn) | |
| if err != nil { | |
| return nil, fmt.Errorf(errWrapFormat, store.ErrAssumeRole, err) | |
| return "", fmt.Errorf(errWrapFormat, store.ErrAssumeRole, err) | |
| // Construct the full parameter name using getKey. | |
| paramName, err := s.getKey(stack, component, key) | |
| if err != nil { | |
| return "", fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | |
| } | |
| // Assume the read role if specified. | |
| cfg, err := s.assumeRole(ctx, s.readRoleArn) | |
| if err != nil { | |
| return "", fmt.Errorf(errWrapFormat, store.ErrAssumeRole, err) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/store/providers/aws_ssm_param_store.go` around lines 426 - 435, Update
the inline comments above getKey and assumeRole in the parameter-name retrieval
flow so each comment ends with a period, without changing the surrounding logic.
Source: Coding guidelines
| func (s *AzureKeyVaultStore) GetRaw(stack string, component string, key string) (string, error) { | ||
| if stack == "" { | ||
| return nil, store.ErrEmptyStack | ||
| return "", store.ErrEmptyStack | ||
| } | ||
| if component == "" { | ||
| return nil, store.ErrEmptyComponent | ||
| return "", store.ErrEmptyComponent | ||
| } | ||
| if key == "" { | ||
| return nil, store.ErrEmptyKey | ||
| return "", store.ErrEmptyKey | ||
| } | ||
|
|
||
| if err := s.ensureClient(); err != nil { | ||
| return nil, err | ||
| return "", err | ||
| } | ||
|
|
||
| secretName, err := s.getKey(stack, component, key) | ||
| if err != nil { | ||
| return nil, fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | ||
| return "", fmt.Errorf(errWrapFormat, store.ErrGetKey, err) | ||
| } | ||
| return s.getRawByName(secretName) | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Support stack-scoped and global secret coordinates.
GetRaw rejects an empty stack or component before getKey can resolve the coordinate. This makes !secret NAME | raw fail for stack-scoped and global secrets. Set has the same validation at lines 293-298, so global writes also fail for Azure Key Vault.
Allow omitted scope segments consistently with GSMStore, then add stack-scoped and global read/write coverage.
The PR objective states that component-less global writes are supported.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/store/providers/azure_keyvault_store.go` around lines 369 - 389, Update
AzureKeyVaultStore.GetRaw and Set to allow empty stack and/or component values
so getKey can resolve stack-scoped and global coordinates consistently with
GSMStore; retain key validation and existing error handling. Ensure !secret NAME
| raw works and component-less global writes are supported, then add coverage
for stack-scoped and global reads and writes.
985daa6 to
2b0a4b6
Compare
40acdd3 to
94c3eee
Compare
what
!secret NAME | rawsupport, including the compact|rawspelling accepted by the shared function tokenizer.!secretstructured-by-default and preserve| pathfor structured lookup; rejectrawcombined withpath.atmos secret setround-trip verbatim instead of storing JSON quote characters.atmos secret set NAME=VALUE --stack ...without--componentonly when Atmos can prove thatNAMEhas one consistentscope: globaldeclaration.!secret.| raw,| path, ordinary strings, multiline keys, masking, and unmasked output.This is 2 of 2 in a secret-handling follow-up stack:
why
Several store backends intentionally JSON-decode values for structured lookup. That is useful for bare
!secretand| path, but it turns valid JSON payloads—including service-account documents, numbers, booleans,null, and JSON-quoted strings—into non-string values. Helm charts expecting a scalar can then receive a map or another unexpected type.The write path had the inverse surprise:
atmos secret setJSON-encoded strings, while an explicit raw read faithfully returned the stored quote characters. Values created externally and values created by Atmos therefore required opposite read behavior.behavior and compatibility
!secretretains its existing structured-value contract.| pathcontinues to select structured data.| rawis additive and explicitly requests the original textual payload.| raw.--componentremains an error for instance- or stack-scoped declarations, missing declarations, or inconsistent global declarations.validation
git diff --checkand a private-name audit pass.references
Summary by CodeRabbit
rawsupport for retrieving secrets as their original textual values.path,raw, anddefaultmodifiers to!secret.!secretsyntax documentation with new options and examples.